---
title: "物件導向程式設計"
description: "學習 Kotlin 的物件導向程式設計特性，與 JavaScript 的類別系統和物件模式進行對比"
---

# 物件導向程式設計

歡迎來到 JavaScript 到 Kotlin 轉換的第五個模組！在本模組中，我們將探索 Kotlin 強大的物件導向程式設計特性，以及它們如何提供比 JavaScript 的類別系統和物件模式更健壯、型別安全的解決方案。

## 學習目標

通過本模組的學習，你將能夠：
- 理解 Kotlin 的類別系統和建構函式模式
- 比較 Kotlin 類別與 JavaScript 類別的差異
- 有效實現繼承和多型
- 使用介面和抽象類別
- 使用資料類別和密封類別
- 理解物件宣告和伴隨物件
- 在 Kotlin 專案中應用 OOP 原則

## 理解 Kotlin 類別

### 類別宣告

Kotlin 提供了比 JavaScript 更簡潔、更強大的類別系統。讓我們從基本的類別宣告開始。

<UniversalEditor title="基本類別宣告" compare={true}>
```javascript !! js
// JavaScript 類別宣告
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    sayHello() {
        console.log(`Hello, my name is ${this.name}`);
    }
    
    getInfo() {
        return `${this.name}, ${this.age} years old`;
    }
}

// 使用
const person = new Person("Alice", 30);
person.sayHello();
console.log(person.getInfo());
```

```kotlin !! kt
// Kotlin 類別宣告
class Person(
    val name: String,
    val age: Int
) {
    fun sayHello() {
        println("Hello, my name is $name")
    }
    
    fun getInfo(): String {
        return "$name, $age years old"
    }
}

// 使用
val person = Person("Alice", 30)
person.sayHello()
println(person.getInfo())
```
</UniversalEditor>

### 主建構函式和次建構函式

Kotlin 提供了比 JavaScript 更靈活的建構函式模式。

<UniversalEditor title="建構函式" compare={true}>
```javascript !! js
// JavaScript 建構函式模式
class Employee {
    constructor(name, department, salary = 50000) {
        this.name = name;
        this.department = department;
        this.salary = salary;
        this.id = Math.random().toString(36).substr(2, 9);
    }
    
    // 靜態方法
    static createManager(name, department) {
        return new Employee(name, department, 80000);
    }
}

// 使用
const emp1 = new Employee("Bob", "Engineering");
const manager = Employee.createManager("Carol", "Management");
```

```kotlin !! kt
// Kotlin 主建構函式和次建構函式
class Employee(
    val name: String,
    val department: String,
    val salary: Int = 50000
) {
    val id: String = generateId()
    
    // 次建構函式
    constructor(name: String, department: String, isManager: Boolean) : 
        this(name, department, if (isManager) 80000 else 50000)
    
    companion object {
        private fun generateId(): String {
            return (0..8).map { ('a'..'z').random() }.joinToString("")
        }
        
        fun createManager(name: String, department: String): Employee {
            return Employee(name, department, true)
        }
    }
}

// 使用
val emp1 = Employee("Bob", "Engineering")
val manager = Employee.createManager("Carol", "Management")
```
</UniversalEditor>

## 繼承和多型

### 類別繼承

Kotlin 的繼承系統比 JavaScript 更明確、型別更安全。

<UniversalEditor title="類別繼承" compare={true}>
```javascript !! js
// JavaScript 繼承
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    makeSound() {
        console.log("Some sound");
    }
    
    getInfo() {
        return `Animal: ${this.name}`;
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    
    makeSound() {
        console.log("Woof! Woof!");
    }
    
    getInfo() {
        return `${super.getInfo()}, Breed: ${this.breed}`;
    }
    
    fetch() {
        console.log(`${this.name} is fetching the ball`);
    }
}

// 使用
const dog = new Dog("Buddy", "Golden Retriever");
dog.makeSound();
console.log(dog.getInfo());
dog.fetch();
```

```kotlin !! kt
// Kotlin 繼承
open class Animal(val name: String) {
    open fun makeSound() {
        println("Some sound")
    }
    
    open fun getInfo(): String {
        return "Animal: $name"
    }
}

class Dog(
    name: String,
    val breed: String
) : Animal(name) {
    
    override fun makeSound() {
        println("Woof! Woof!")
    }
    
    override fun getInfo(): String {
        return "${super.getInfo()}, Breed: $breed"
    }
    
    fun fetch() {
        println("$name is fetching the ball")
    }
}

// 使用
val dog = Dog("Buddy", "Golden Retriever")
dog.makeSound()
println(dog.getInfo())
dog.fetch()
```
</UniversalEditor>

### 抽象類別

Kotlin 的抽象類別為繼承提供了更結構化的方法。

<UniversalEditor title="抽象類別" compare={true}>
```javascript !! js
// JavaScript 抽象類別模式
class Shape {
    constructor(color) {
        if (new.target === Shape) {
            throw new Error("Shape is abstract and cannot be instantiated");
        }
        this.color = color;
    }
    
    getArea() {
        throw new Error("getArea must be implemented");
    }
    
    getPerimeter() {
        throw new Error("getPerimeter must be implemented");
    }
    
    getInfo() {
        return `Shape: ${this.constructor.name}, Color: ${this.color}`;
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    getArea() {
        return Math.PI * this.radius * this.radius;
    }
    
    getPerimeter() {
        return 2 * Math.PI * this.radius;
    }
}

// 使用
const circle = new Circle("red", 5);
console.log(circle.getArea());
console.log(circle.getPerimeter());
```

```kotlin !! kt
// Kotlin 抽象類別
abstract class Shape(val color: String) {
    abstract fun getArea(): Double
    abstract fun getPerimeter(): Double
    
    fun getInfo(): String {
        return "Shape: ${this::class.simpleName}, Color: $color"
    }
}

class Circle(color: String, val radius: Double) : Shape(color) {
    override fun getArea(): Double {
        return Math.PI * radius * radius
    }
    
    override fun getPerimeter(): Double {
        return 2 * Math.PI * radius
    }
}

// 使用
val circle = Circle("red", 5.0)
println(circle.getArea())
println(circle.getPerimeter())
```
</UniversalEditor>

## 介面

### 介面實現

Kotlin 的介面比 JavaScript 更強大，支援預設實現和屬性。

<UniversalEditor title="介面" compare={true}>
```javascript !! js
// JavaScript 介面模式
class Drawable {
    draw() {
        throw new Error("draw method must be implemented");
    }
}

class Movable {
    move() {
        throw new Error("move method must be implemented");
    }
}

class GameObject extends Drawable {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    draw() {
        console.log(`Drawing at position (${this.x}, ${this.y})`);
    }
    
    move(dx, dy) {
        this.x += dx;
        this.y += dy;
        console.log(`Moved to (${this.x}, ${this.y})`);
    }
}

// 使用
const gameObject = new GameObject(10, 20);
gameObject.draw();
gameObject.move(5, 10);
```

```kotlin !! kt
// Kotlin 介面
interface Drawable {
    fun draw()
}

interface Movable {
    fun move(dx: Double, dy: Double)
}

class GameObject(
    var x: Double,
    var y: Double
) : Drawable, Movable {
    
    override fun draw() {
        println("Drawing at position ($x, $y)")
    }
    
    override fun move(dx: Double, dy: Double) {
        x += dx
        y += dy
        println("Moved to ($x, $y)")
    }
}

// 使用
val gameObject = GameObject(10.0, 20.0)
gameObject.draw()
gameObject.move(5.0, 10.0)
```
</UniversalEditor>

### 帶預設實現的介面

Kotlin 介面可以有預設實現，這與 JavaScript 不同。

<UniversalEditor title="介面預設實現" compare={true}>
```javascript !! js
// JavaScript mixin 模式
const LoggerMixin = {
    log(message) {
        console.log(`[${this.constructor.name}] ${message}`);
    }
};

class UserService {
    constructor() {
        Object.assign(this, LoggerMixin);
    }
    
    getUser(id) {
        this.log(`Fetching user with id: ${id}`);
        return { id, name: "User" };
    }
}

// 使用
const userService = new UserService();
userService.getUser(123);
```

```kotlin !! kt
// Kotlin 帶預設實現的介面
interface Logger {
    fun log(message: String) {
        println("[${this::class.simpleName}] $message")
    }
}

class UserService : Logger {
    fun getUser(id: Int): User {
        log("Fetching user with id: $id")
        return User(id, "User")
    }
}

data class User(val id: Int, val name: String)

// 使用
val userService = UserService()
userService.getUser(123)
```
</UniversalEditor>

## 資料類別

### 資料類別與普通物件

Kotlin 的資料類別提供常見方法的自動實現。

<UniversalEditor title="資料類別" compare={true}>
```javascript !! js
// JavaScript 普通物件
const user = {
    id: 1,
    name: "Alice",
    email: "alice@example.com"
};

// 手動實現 toString, equals 等
class User {
    constructor(id, name, email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    toString() {
        return `User(id=${this.id}, name='${this.name}', email='${this.email}')`;
    }
    
    equals(other) {
        if (!(other instanceof User)) return false;
        return this.id === other.id && 
               this.name === other.name && 
               this.email === other.email;
    }
    
    copy(updates = {}) {
        return new User(
            updates.id !== undefined ? updates.id : this.id,
            updates.name !== undefined ? updates.name : this.name,
            updates.email !== undefined ? updates.email : this.email
        );
    }
}

// 使用
const user1 = new User(1, "Alice", "alice@example.com");
const user2 = user1.copy({ name: "Alice Updated" });
console.log(user1.toString());
console.log(user1.equals(user2));
```

```kotlin !! kt
// Kotlin 資料類別
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// 使用
val user1 = User(1, "Alice", "alice@example.com")
val user2 = user1.copy(name = "Alice Updated")
println(user1.toString())
println(user1 == user2)
```
</UniversalEditor>

### 資料類別特性

資料類別提供許多有用的特性。

<UniversalEditor title="資料類別特性" compare={true}>
```javascript !! js
// JavaScript 手動實現
class Product {
    constructor(id, name, price, category) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }
    
    // 解構
    static destructure(product) {
        const { id, name, price, category } = product;
        return [id, name, price, category];
    }
    
    // 元件比較
    equals(other) {
        return this.id === other.id &&
               this.name === other.name &&
               this.price === other.price &&
               this.category === other.category;
    }
    
    // 雜湊碼
    hashCode() {
        return this.id * 31 + this.name.length;
    }
}

// 使用
const product = new Product(1, "Laptop", 999.99, "Electronics");
const [id, name, price, category] = Product.destructure(product);
console.log(`ID: ${id}, Name: ${name}, Price: ${price}, Category: ${category}`);
```

```kotlin !! kt
// Kotlin 資料類別自動特性
data class Product(
    val id: Int,
    val name: String,
    val price: Double,
    val category: String
)

// 使用
val product = Product(1, "Laptop", 999.99, "Electronics")
val (id, name, price, category) = product // 解構
println("ID: $id, Name: $name, Price: $price, Category: $category")

// 自動 equals, hashCode, toString, copy
val product2 = product.copy(price = 899.99)
println(product == product2)
```
</UniversalEditor>

## 密封類別

### 密封類別的型別安全

Kotlin 的密封類別為層次結構提供編譯時型別安全。

<UniversalEditor title="密封類別" compare={true}>
```javascript !! js
// JavaScript 聯合型別模式
class Result {
    static success(data) {
        return new SuccessResult(data);
    }
    
    static error(message) {
        return new ErrorResult(message);
    }
}

class SuccessResult extends Result {
    constructor(data) {
        super();
        this.data = data;
    }
    
    isSuccess() {
        return true;
    }
    
    isError() {
        return false;
    }
}

class ErrorResult extends Result {
    constructor(message) {
        super();
        this.message = message;
    }
    
    isSuccess() {
        return false;
    }
    
    isError() {
        return true;
    }
}

// 使用
function processResult(result) {
    if (result.isSuccess()) {
        console.log("Success:", result.data);
    } else {
        console.log("Error:", result.message);
    }
}

const successResult = Result.success({ id: 1, name: "User" });
const errorResult = Result.error("User not found");
processResult(successResult);
processResult(errorResult);
```

```kotlin !! kt
// Kotlin 密封類別
sealed class Result {
    data class Success(val data: Any) : Result()
    data class Error(val message: String) : Result()
}

// 使用 when 表達式
fun processResult(result: Result) {
    when (result) {
        is Result.Success -> println("Success: ${result.data}")
        is Result.Error -> println("Error: ${result.message}")
    }
}

val successResult = Result.Success(mapOf("id" to 1, "name" to "User"))
val errorResult = Result.Error("User not found")
processResult(successResult)
processResult(errorResult)
```
</UniversalEditor>

## 物件宣告和伴隨物件

### 單例物件

Kotlin 的物件宣告提供了實現單例的簡潔方法。

<UniversalEditor title="單例物件" compare={true}>
```javascript !! js
// JavaScript 單例模式
class DatabaseConnection {
    constructor() {
        if (DatabaseConnection.instance) {
            return DatabaseConnection.instance;
        }
        this.connection = null;
        DatabaseConnection.instance = this;
    }
    
    connect() {
        if (!this.connection) {
            this.connection = { status: "connected" };
            console.log("Database connected");
        }
        return this.connection;
    }
    
    disconnect() {
        if (this.connection) {
            this.connection = null;
            console.log("Database disconnected");
        }
    }
}

// 使用
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true
db1.connect();
db2.disconnect();
```

```kotlin !! kt
// Kotlin 物件宣告（單例）
object DatabaseConnection {
    private var connection: Any? = null
    
    fun connect(): Any {
        if (connection == null) {
            connection = mapOf("status" to "connected")
            println("Database connected")
        }
        return connection!!
    }
    
    fun disconnect() {
        if (connection != null) {
            connection = null
            println("Database disconnected")
        }
    }
}

// 使用
val db1 = DatabaseConnection
val db2 = DatabaseConnection
println(db1 === db2) // true
db1.connect()
db2.disconnect()
```
</UniversalEditor>

### 伴隨物件

伴隨物件在 Kotlin 中提供類似靜態的功能。

<UniversalEditor title="伴隨物件" compare={true}>
```javascript !! js
// JavaScript 靜態方法
class MathUtils {
    static PI = 3.14159;
    
    static add(a, b) {
        return a + b;
    }
    
    static multiply(a, b) {
        return a * b;
    }
    
    static createRandom() {
        return Math.random();
    }
}

// 使用
console.log(MathUtils.PI);
console.log(MathUtils.add(5, 3));
console.log(MathUtils.multiply(4, 7));
console.log(MathUtils.createRandom());
```

```kotlin !! kt
// Kotlin 伴隨物件
class MathUtils {
    companion object {
        const val PI = 3.14159
        
        fun add(a: Double, b: Double): Double {
            return a + b
        }
        
        fun multiply(a: Double, b: Double): Double {
            return a * b
        }
        
        fun createRandom(): Double {
            return Math.random()
        }
    }
}

// 使用
println(MathUtils.PI)
println(MathUtils.add(5.0, 3.0))
println(MathUtils.multiply(4.0, 7.0))
println(MathUtils.createRandom())
```
</UniversalEditor>

## 進階 OOP 模式

### 擴展函式

Kotlin 的擴展函式允許為現有類別添加功能。

<UniversalEditor title="擴展函式" compare={true}>
```javascript !! js
// JavaScript 原型擴展
String.prototype.reverse = function() {
    return this.split('').reverse().join('');
};

String.prototype.capitalize = function() {
    return this.charAt(0).toUpperCase() + this.slice(1);
};

// 使用
const text = "hello world";
console.log(text.reverse());
console.log(text.capitalize());
```

```kotlin !! kt
// Kotlin 擴展函式
fun String.reverse(): String {
    return this.reversed()
}

fun String.capitalize(): String {
    return this.replaceFirstChar { it.uppercase() }
}

// 使用
val text = "hello world"
println(text.reverse())
println(text.capitalize())
```
</UniversalEditor>

### 運算子重載

Kotlin 允許為自定義型別重載運算子。

<UniversalEditor title="運算子重載" compare={true}>
```javascript !! js
// JavaScript 運算子重載模擬
class Vector {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    
    add(other) {
        return new Vector(this.x + other.x, this.y + other.y);
    }
    
    multiply(scalar) {
        return new Vector(this.x * scalar, this.y * scalar);
    }
    
    toString() {
        return `Vector(${this.x}, ${this.y})`;
    }
}

// 使用
const v1 = new Vector(1, 2);
const v2 = new Vector(3, 4);
const result = v1.add(v2);
const scaled = v1.multiply(2);
console.log(result.toString());
console.log(scaled.toString());
```

```kotlin !! kt
// Kotlin 運算子重載
data class Vector(val x: Double, val y: Double) {
    operator fun plus(other: Vector): Vector {
        return Vector(x + other.x, y + other.y)
    }
    
    operator fun times(scalar: Double): Vector {
        return Vector(x * scalar, y * scalar)
    }
}

// 使用
val v1 = Vector(1.0, 2.0)
val v2 = Vector(3.0, 4.0)
val result = v1 + v2
val scaled = v1 * 2.0
println(result)
println(scaled)
```
</UniversalEditor>

## 實踐範例

### 構建簡單的銀行系統

讓我們創建一個展示 Kotlin OOP 特性的實踐範例。

<UniversalEditor title="銀行系統範例" compare={true}>
```javascript !! js
// JavaScript 銀行系統
class Account {
    constructor(accountNumber, balance = 0) {
        this.accountNumber = accountNumber;
        this.balance = balance;
        this.transactions = [];
    }
    
    deposit(amount) {
        if (amount > 0) {
            this.balance += amount;
            this.transactions.push({
                type: 'deposit',
                amount: amount,
                timestamp: new Date()
            });
            return true;
        }
        return false;
    }
    
    withdraw(amount) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
            this.transactions.push({
                type: 'withdraw',
                amount: amount,
                timestamp: new Date()
            });
            return true;
        }
        return false;
    }
    
    getBalance() {
        return this.balance;
    }
    
    getTransactionHistory() {
        return [...this.transactions];
    }
}

class SavingsAccount extends Account {
    constructor(accountNumber, balance = 0, interestRate = 0.02) {
        super(accountNumber, balance);
        this.interestRate = interestRate;
    }
    
    addInterest() {
        const interest = this.balance * this.interestRate;
        this.deposit(interest);
        return interest;
    }
}

// 使用
const account = new SavingsAccount("12345", 1000, 0.05);
account.deposit(500);
account.withdraw(200);
account.addInterest();
console.log("Balance:", account.getBalance());
console.log("Transactions:", account.getTransactionHistory());
```

```kotlin !! kt
// Kotlin 銀行系統
sealed class Transaction {
    data class Deposit(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
    data class Withdraw(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
    data class Interest(val amount: Double, val timestamp: Long = System.currentTimeMillis()) : Transaction()
}

open class Account(
    val accountNumber: String,
    protected var balance: Double = 0.0
) {
    private val transactions = mutableListOf<Transaction>()
    
    fun deposit(amount: Double): Boolean {
        return if (amount > 0) {
            balance += amount
            transactions.add(Transaction.Deposit(amount))
            true
        } else false
    }
    
    fun withdraw(amount: Double): Boolean {
        return if (amount > 0 && amount <= balance) {
            balance -= amount
            transactions.add(Transaction.Withdraw(amount))
            true
        } else false
    }
    
    fun getBalance(): Double = balance
    
    fun getTransactionHistory(): List<Transaction> = transactions.toList()
}

class SavingsAccount(
    accountNumber: String,
    balance: Double = 0.0,
    private val interestRate: Double = 0.02
) : Account(accountNumber, balance) {
    
    fun addInterest(): Double {
        val interest = balance * interestRate
        deposit(interest)
        return interest
    }
}

// 使用
val account = SavingsAccount("12345", 1000.0, 0.05)
account.deposit(500.0)
account.withdraw(200.0)
account.addInterest()
println("Balance: ${account.getBalance()}")
println("Transactions: ${account.getTransactionHistory()}")
```
</UniversalEditor>

## 最佳實踐和模式

### 何時使用不同的類別型別

<UniversalEditor title="類別型別指南" compare={true}>
```javascript !! js
// JavaScript 類別使用指南
// 1. 常規類別用於具有行為的複雜物件
class UserService {
    constructor() {
        this.users = new Map();
    }
    
    addUser(user) {
        this.users.set(user.id, user);
    }
    
    getUser(id) {
        return this.users.get(id);
    }
}

// 2. 普通物件用於簡單資料結構
const userData = {
    id: 1,
    name: "Alice",
    email: "alice@example.com"
};

// 3. 工廠函式用於物件創建
function createUser(id, name, email) {
    return {
        id,
        name,
        email,
        toString() {
            return `User(id=${this.id}, name='${this.name}')`;
        }
    };
}
```

```kotlin !! kt
// Kotlin 類別使用指南
// 1. 常規類別用於具有行為的複雜物件
class UserService {
    private val users = mutableMapOf<Int, User>()
    
    fun addUser(user: User) {
        users[user.id] = user
    }
    
    fun getUser(id: Int): User? {
        return users[id]
    }
}

// 2. 資料類別用於簡單資料結構
data class UserData(
    val id: Int,
    val name: String,
    val email: String
)

// 3. 物件宣告用於單例
object UserFactory {
    fun createUser(id: Int, name: String, email: String): UserData {
        return UserData(id, name, email)
    }
}
```
</UniversalEditor>

## 總結

在本模組中，我們探索了 Kotlin 強大的物件導向程式設計特性：

### 關鍵要點

1. **類別宣告**: Kotlin 的主建構函式語法比 JavaScript 的類別建構函式更簡潔
2. **繼承**: Kotlin 使用 `open` 關鍵字和顯式繼承 `:`
3. **介面**: Kotlin 介面支援預設實現和屬性
4. **資料類別**: 自動生成 `equals()`、`hashCode()`、`toString()` 和 `copy()`
5. **密封類別**: 類別層次結構的編譯時型別安全
6. **物件宣告**: 簡潔的單例實現
7. **伴隨物件**: 類別內的靜態功能
8. **擴展函式**: 為現有類別添加功能
9. **運算子重載**: 用戶定義型別的自定義運算子

### 與 JavaScript 的對比

| 特性 | JavaScript | Kotlin |
|------|------------|--------|
| 類別宣告 | `class` 帶建構函式 | 主建構函式語法 |
| 繼承 | `extends` | `:` 帶 `open` 關鍵字 |
| 介面 | 無原生支援 | 完整介面支援 |
| 資料類別 | 手動實現 | 使用 `data class` 自動實現 |
| 單例 | 手動模式 | `object` 宣告 |
| 靜態方法 | `static` 關鍵字 | `companion object` |
| 擴展方法 | 原型修改 | 擴展函式 |

### 下一步

在下一個模組中，我們將探索 Kotlin 的 Android 開發，包括：
- Android 專案結構
- Activity 和 Fragment 生命週期
- 佈局和 UI 元件
- 事件處理和資料綁定
- 使用 Jetpack Compose 的現代 Android 開發

Kotlin OOP 特性的這個基礎對於構建健壯的 Android 應用程式和理解現代 Kotlin 開發模式至關重要。 